這邊實現簡單的 Parser,其中一個重要的概念不可變 Immutable + 結構共享 Structural Sharing,減少複製、簡化快取一致性。
成本:更多小物件 GC 壓力 -> 以 Pool + Lazy + table-driven 緩解。
其中的 Syntax Trees,Lexer 掃描 SourceText 建立 token,Parser 使用遞迴下降,帶表驅動優化建立樹。
using System;
using System.Collections.Generic;
namespace MiniRoslyn.Syntax
{
    // 節點基類 - 模仿 Green Node 概念(不可變)
    public abstract record Node;
    public record NumberNode(int Value) : Node;
    public record BinaryNode(string Op, Node Left, Node Right) : Node;
    public static class MiniParser
    {
        // 極簡:只處理 整數 + 加減乘
        public static Node Parse(string text)
        {
            var tokens = Tokenize(text);
            int pos = 0;
            return ParseExpr();
            Node ParseExpr()
            {
                Node node = ParseTerm();
                while (Match("+") || Match("-"))
                {
                    string op = tokens[pos - 1];
                    var right = ParseTerm();
                    node = new BinaryNode(op, node, right);
                }
                return node;
            }
            Node ParseTerm()
            {
                Node node = ParseFactor();
                while (Match("*"))
                {
                    string op = tokens[pos - 1];
                    var right = ParseFactor();
                    node = new BinaryNode(op, node, right);
                }
                return node;
            }
            Node ParseFactor()
            {
                if (Match("("))
                {
                    var e = ParseExpr();
                    Expect(")");
                    return e;
                }
                if (int.TryParse(Peek(), out var v))
                {
                    pos++;
                    return new NumberNode(v);
                }
                throw new Exception("Unexpected token: " + Peek());
            }
            bool Match(string t)
            {
                if (Peek() == t) { pos++; return true; }
                return false;
            }
            void Expect(string t)
            {
                if (!Match(t)) throw new Exception("Expect " + t);
            }
            string Peek() => pos < tokens.Count ? tokens[pos] : "<eof>";
        }
        static List<string> Tokenize(string text)
        {
            var list = new List<string>();
            for (int i = 0; i < text.Length; i++)
            {
                char c = text[i];
                if (char.IsWhiteSpace(c)) continue;
                if (char.IsDigit(c))
                {
                    int j = i;
                    while (j < text.Length && char.IsDigit(text[j])) j++;
                    list.Add(text[i..j]);
                    i = j - 1;
                    continue;
                }
                if ("+-*()".IndexOf(c) >= 0)
                {
                    list.Add(c.ToString());
                    continue;
                }
                throw new Exception("Invalid char: " + c);
            }
            return list;
        }
    }
}